package com.menghao.rpc.spring;

import com.menghao.rpc.consumer.annotation.Reference;
import com.menghao.rpc.consumer.discovery.ReferenceRepository;
import com.menghao.rpc.consumer.model.ReferenceKey;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessor;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;

/**
 * <p>@Reference注解增强：注入代理对象.</br>
 * <p>时机：属性填充时，代理对象由JDK动态代理实现</p>
 *
 * @author MarvelCode
 * @see ReferenceRepository
 */
public class ReferenceAutowired implements InstantiationAwareBeanPostProcessor {

    private ReferenceRepository referenceRepository;

    public ReferenceAutowired(ReferenceRepository referenceRepository) {
        this.referenceRepository = referenceRepository;
    }

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        return null;
    }

    @Override
    public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
        return true;
    }

    @Override
    public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
        Class sourceClass = bean.getClass();
        do {
            // 遍历成员变量找出所有被 @Reference注解的成员进行注入
            for (Field field : sourceClass.getDeclaredFields()) {
                Reference reference = field.getAnnotation(Reference.class);
                if (reference != null) {
                    // 找到对应的代理对象并赋值
                    Object candidate = findReference(field.getType(), reference);
                    field.setAccessible(true);
                    try {
                        field.set(bean, candidate);
                    } catch (IllegalAccessException e) {
                        throw new BeanCreationException(beanName, "@Reference dependencies autowired failed", e);
                    }
                }
            }
            sourceClass = sourceClass.getSuperclass();
        } while (sourceClass != null && sourceClass != Object.class);
        return pvs;
    }

    private Object findReference(Class sourceInterface, Reference reference) {
        // 根据注入类型和指定实现
        ReferenceKey referenceKey = new ReferenceKey(sourceInterface, reference.value());
        return referenceRepository.getReference(referenceKey);
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}
